﻿using System;
using System.Collections.Generic;
using Ur;
using Pixels.Logic.Structures;


namespace Pixels.Logic
{

    public abstract class Structure : TileEntity, IAssignationTarget
    {
        internal virtual void executeTurn() {
            gameplayEffect = new OverallTurnEffect();
            calculateEffects();
            applyEffects(myColony);
        }

        public StructureType MyType;

        internal Colony myColony;

        internal abstract string getName();

        /// <summary> Do not modify the contents of this collection. </summary>
        internal HashSet<Colonist> AssignedColonists = new HashSet<Colonist>();

        /// <summary> You do not call this. Colonist calls this </summary>
        public void loseColonist(Colonist colonist) {
            AssignedColonists.Remove(colonist);
        }

        /// <summary> Do not modify the contents of this class. </summary>        
        public void gainColonist(Colonist colonist) {
            AssignedColonists.Add(colonist);
        }

        /// <summary> what effects does it have - e.g. resource delta, lebensraum delta etc. </summary>
        public OverallTurnEffect gameplayEffect = new OverallTurnEffect();
        
        abstract internal void calculateEffects();

        /// <summary> You do not call this. Colony calls this. </summary>
        /// <param name="ofType"></param>
        /// <returns></returns>
        static internal Structure Spawn(StructureType ofType) {
            var className = GlobalConsts.getStructureConsts(ofType).className;
            Type type = System.Type.GetType("Pixels.Logic.Structures." + className);
            if (type == null) throw new Exception("Cannot find class named " + className + " through reflection");
            var structure = (Structure)Activator.CreateInstance(type);
            structure.MyType = ofType;
            return structure;
        }

        void applyEffects(Colony colony) {

            ResourcePool deficits;
            colony.accumulatedResources.addPoolToPoolAndCheckDeficits(this.gameplayEffect.colonyResourceDelta, out deficits);
            colony.totalLebensraum += this.gameplayEffect.addedLivingSpace;
            colony.totalResearch   += this.gameplayEffect.addedResearch;
            World.Logic.injectEvents(gameplayEffect.events);

        }

        public StructureConsts MyConsts {
            get {
                return GlobalConsts.getStructureConsts(this.MyType);                
            }
        }


        protected int countAssignedColonists(Colonist.Occupation occupation) {
            int counter = 0;
            foreach (var colonist in AssignedColonists) if (colonist.MyOccupation == occupation) counter++;
            return counter;
        }

        virtual protected bool isColonistTypeAssignable(Colonist.Occupation occupation) {
            return false;
        }

        virtual internal int maxSlots() {
            return 0;
        }

        protected void giveResource(Resource res, int amount) {
            gameplayEffect.colonyResourceDelta.changeResource(res, amount);
        }
        protected void spendResource(Resource res, int amount) {
            gameplayEffect.colonyResourceDelta.changeResource(res, -amount);
        }



        internal void damage() {
            if (this.damaged) this.destroyed = true;
            this.damaged = true;
            
        }

        public bool damaged { get; private set; }
        public bool destroyed { get; set; }

        internal virtual bool canAccomodateColonist(Colonist colonist) {
            return (this.isColonistTypeAssignable(colonist.MyOccupation) && this.AssignedColonists.Count < this.maxSlots()) ;            
        }

        int IAssignationTarget.numColonists() {
            return AssignedColonists.Count;
        }
        IEnumerable<Colonist> IAssignationTarget.ListColonists() {
            return AssignedColonists;
        }

        Tile IAssignationTarget.getTile() { return this.OnTile; }
    }
}
